#include <queue>
#include <pthread.h>
using namespace std;

//模板类型 支持不同场景
template <class T>
class block_queue
{
public:
    block_queue(size_t queue_size = 5)
        : _queue(), _queue_size(queue_size)
    {
        pthread_mutex_init(&_queue_mutex, nullptr);
        pthread_cond_init(&_consumer_condition, nullptr);
        pthread_cond_init(&_producer_condition, nullptr);
    }
    ~block_queue()
    {
        pthread_mutex_destroy(&_queue_mutex);
        pthread_cond_destroy(&_consumer_condition);
        pthread_cond_destroy(&_producer_condition);
    }

public:
    bool is_full()
    {
        return _queue.size() == _queue_size;
    }
    bool is_empty()
    {
        return _queue.size() == 0;
    }
    void push(T &product)
    {
        // queue本身应时临界资源,因此生产者消费者都需要串行访问它
        pthread_mutex_lock(&_queue_mutex);
        //临界区
        {
            //生产者先判断队列是否满了,如果满了,就阻塞等待被唤醒
            //此时时拿着锁的,如果刚解锁,消费者消费了就可能会立刻被唤醒
            while (is_full())
            { //用while防止伪唤醒(系统或者代码问题),提高健壮性.
                
                //_queue.pop();//可以选择不等待条件,只保留最新的产品
                pthread_cond_wait(&_producer_condition, &_queue_mutex);
            }
            //被唤醒代表又拿到了锁
            _queue.push(product);
        }
        //退出临界区解锁
        pthread_mutex_unlock(&_queue_mutex);
        //解锁后通知消费者 解锁前通知也可以,但是严格来讲,这不应在临界区内.
        //为了并发效率,原则上不在临界区内执行与对临界资源进行原子操作无关的代码
        pthread_cond_signal(&_consumer_condition);
    }
    T pop()
    {
        // queue本身应时临界资源,因此生产者消费者都需要串行访问它
        pthread_mutex_lock(&_queue_mutex);
        T ret=T();
        //临界区
        {
            while (is_empty())
            {
                pthread_cond_wait(&_consumer_condition, &_queue_mutex);
            }
            ret = _queue.front();
            _queue.pop();
        }
        //退出临界区解锁
        pthread_mutex_unlock(&_queue_mutex);
        //至少有了一个空位,通知生产者
        pthread_cond_signal(&_producer_condition);
        //这里三次拷贝构造,想提高效率可以不使用STL
        return ret;
    }

private:
    queue<T> _queue;
    size_t _queue_size;
    pthread_mutex_t _queue_mutex;
    pthread_cond_t _consumer_condition;
    pthread_cond_t _producer_condition;
};